/**
 * Copyright 2019 Inc.
 **/
package com.myz.service.feign.config;

import com.alibaba.fastjson.JSON;
import feign.*;
import feign.codec.ErrorDecoder;
import feign.slf4j.Slf4jLogger;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.io.IOException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Map;

/**
 * @author maoyz0621 on 19-12-17
 * @version: v1.0
 */
@Configuration
@ConditionalOnClass({Feign.class})
@AutoConfigureBefore(FeignClientsConfiguration.class)
public class FeignConfig {

    /**
     * 默认
     *
     * @return
     */
    @Bean
    public ErrorDecoder errorDecoder() {
        return new UserErrorDecoder();
    }

    /**
     * feign 使用Slf4j日志
     *
     * @return
     */
    @Bean
    public Logger logger() {
        return new Slf4jLogger();
    }

    /**
     * 失败重试机制, 重试两次
     * Feign的重试机制在源码中默认关闭的，因为Ribbon的重试机制和Feign原来的重试机制冲突，所以在一般情况下，Feign的重试机制指的就是Ribbon的重试机制
     * <p>
     * # Ribbon超时重试配置
     * ribbon.ConnectTimeout=5000   # 连接超时时间
     * ribbon.ReadTimeout=5000     # 逻辑处理超时时间
     * ribbon.OkToRetryOnAllOperations=true    # 是否对所有操作都进行重试
     * ribbon.MaxAutoRetries=2     # 对当前实例的最大重试次数(请求服务超时6s则会再请求一次)
     * ribbon.MaxAutoRetriesNextServer=1   # 切换实例的最大重试次数(如果还失败就切换下
     *
     * @return
     */
    @Bean
    public Retryer retryer() {
        // return new Retryer.Default(100, 2000, 2);
        return Retryer.NEVER_RETRY;
    }

    /**
     * feign请求超时设置, 使用ribbon的设置时间
     * 默认  this(10 * 1000, 60 * 1000);
     *
     * @return
     */
    @Bean
    public Request.Options options(ConfigurableEnvironment environment) {
        int readTimeout = environment.getProperty("ribbon.ReadTimeout", int.class, 6000);
        int connectTimeout = environment.getProperty("ribbon.ConnectTimeout", int.class, 6000);
        return new Request.Options(connectTimeout, readTimeout);
    }

    /**
     * 指定日志打印信息
     * NONE（默认）	不记录任何日志
     * BASIC	    仅记录请求方法，URL，响应状态代码以及执行时间（适合生产环境）
     * HEADERS	    记录BASIC级别的基础上，记录请求和响应的header
     * FULL	        记录请求和弦ineader，body和元数据
     *
     * @return
     */
    @Bean
    public Logger.Level level() {
        return Logger.Level.FULL;
    }

    /**
     * 只要被扫描的类包含了@Controller注解或@RequestMapping注解，那么就会被加载进来
     * 虽然@FeignClient定义修饰的服务消费端没有声明这些注解，但是当我们使用了继承特性的时候，那么这些注解就也会被服务消费者解析和加载
     */
    @Bean
    public WebMvcRegistrations feignWebRegistrations() {

        return new WebMvcRegistrations() {
            @Override
            public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
                return new FeignRequestMappingHandlerMapping();
            }
        };
    }

    /**
     * feign 拦截器
     *
     * @return
     */
    // @Bean
    public RequestInterceptor requestInterceptor() {
        return new FeignConfig.FeignBasicAuthRequestInterceptor();
    }

    /**
     * RequestInterceptor接口, 完成对所有的Feign请求, 传递请求头和请求参数
     */
    private class FeignBasicAuthRequestInterceptor implements RequestInterceptor {

        private final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(FeignConfig.FeignBasicAuthRequestInterceptor.class);

        @Override
        public void apply(RequestTemplate template) {

            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();

            // 此处为什么attributes = null?
            HttpServletRequest request = attributes.getRequest();
            Enumeration<String> headerNames = request.getHeaderNames();
            if (headerNames != null) {
                while (headerNames.hasMoreElements()) {
                    String name = headerNames.nextElement();
                    if (StringUtils.equals("Authorization", name)) {
                        String values = request.getHeader(name);
                        template.header(name, values);
                    }
                }
            }

            String contentType = request.getContentType();
            System.out.println(contentType);

            // 添加参数
            Map<String, String[]> parameterMap = request.getParameterMap();
            logger.info("************* 请求body = {} ********************", JSON.toJSONString(parameterMap));

            // 处理文件信息
            try {
                if (request instanceof MultipartHttpServletRequest) {
                    Collection<Part> parts = request.getParts();
                    System.out.println(parts);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ServletException e) {
                e.printStackTrace();
            }
            logger.info("");


        }
    }

    /**
     *
     */
    private static class FeignRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

        /**
         * 不能被@FeignClient注解修饰的类才会进行解析加载
         *
         * @param beanType
         * @return
         */
        @Override
        protected boolean isHandler(Class<?> beanType) {
            return super.isHandler(beanType) && !AnnotatedElementUtils.hasAnnotation(beanType, FeignClient.class);
        }
    }
}
